今天的文章分為兩部分,一個是今天做完的部分修改,一部份是參賽感想
我目前規劃做到的部分大概先暫時到從Line進行完整的購物流程,亦即
這部分在今天繼續更新一些功能上的修改
現在會拒絕庫存不足的品項加入購物車
def Control_Shopping_Cart_ViaMessageText(uid, user_type_text):
    split_text = user_type_text.split(' ')
    if(len(split_text) > 3):return "錯誤的購物車指令\ncart \{要加入或變更的產品ID\} \{該產品的數量\}\n輸入數字不需要大括弧\{\}"
    if(not split_text[1].isnumeric()):return "請輸入羅馬數字的產品ID"
    scid = dbpm.INS_QUY_SC(uid)
    if((split_text[2][0] == '+' or split_text[2][0] == '-') and split_text[2][1:].isnumeric()):
        num = int(split_text[2])
        new_qt = dbpm.QUY_Shopping_Cart_item_Quantity(split_text[1], scid) + num
    elif(split_text[2].isnumeric()):
        new_qt = int(split_text[2])
    else:
        return "請直接輸入訂購數量的羅馬數字,或是+/-符號加數字"
    if(new_qt < 0):
        new_qt = 0
    # app.logger.debug(f"{split_text[1]}, {new_qt}")
    p_name, p_price = dbpm.QUY_Prod_Name_and_Price_by_pid(split_text[1])
    current_quantity = dbpm.QUY_Prod_Quantity_by_pid(split_text[1])
    if(current_quantity >= new_qt):
        dbpm.INS_UPD_Prod_to_Cart(scid, split_text[1], new_qt)
        if(new_qt == 0):
            dbpm.DEL_Shopping_Cart_items(scid)
            return f"已將{p_name}自購物車中刪除"
        else:
            return f"已將{p_name}(單價:{p_price})的購買數量設定為{new_qt}"
    else:
        return f"{p_name}目前的庫存不足,無法加入購物車"
現在付款ID不會再綁定訂單,在付款成功前,使用者可以切換用什麼付款,以及加入庫存更新 & 購物車內物品庫存檢查,建立訂單流程:
# Server.py
elif(datapath == "action=buy"):
    if(not datavalue):
        isSucc, scidormsg = Handler.MakeOrder_1_Check_Cart(event.source.user_id)
        app.logger.debug(f"建立訂單-檢查購物車, {isSucc}, {scidormsg}")
        if(isSucc):
            isSucc, oidormsg = Handler.MakeOrder_2_Create_Order(scidormsg, event.source.user_id)
            if(isSucc):
                template_msg = APIModel.OrderPaySelTemp(scidormsg, oidormsg)
                line_bot_api.reply_message(
                    event.reply_token,
                    template_msg
                )
            else:
                line_bot_api.reply_message(
                    event.reply_token,
                    TextSendMessage(text=oidormsg)
                ) 
        else:
            line_bot_api.reply_message(
                event.reply_token,
                TextSendMessage(text=scidormsg)
            )
    else:
        oid = datavalue.get('oid')[0]
        scid = datavalue.get('scid')[0]
        paytype = datavalue.get('paytype')[0]
        app.logger.debug(f"訂單:{oid}(scid:{scid}), 選擇付款方式:{paytype}")
        if(oid and scid and paytype):
            isSucc, msg = Handler.MakeOrder_3_Request_Pay(oid, scid, paytype)
            if(isSucc):
                if(paytype == "1"):
                    app.logger.debug(f"銀行轉帳(ATM)付款資訊:{msg}")
                elif(paytype == "2"):
                    app.logger.debug(f"信用卡付款資訊:{msg}")
                    template_msg = APIModel.OrderPayURLTemp(msg)
                    line_bot_api.reply_message(
                        event.reply_token,
                        template_msg
                    )
            else:
                line_bot_api.reply_message(
                    event.reply_token,
                    TextSendMessage(text=msg)
                )
        else:
            app.logger.debug("建立訂單時參數錯誤", datavalue)
# OrderHandler.py
def MakeOrder_1_Check_Cart(uid):
    scid = dbpm.INS_QUY_SC(uid)
    isSucc, msg = CheckQuantity(scid)
    if(isSucc):
        dbpm.UPD_Shopping_Cart_lock_bY_scid(True, scid)
        return True, scid
    else:
        return False, msg
def CheckQuantity(scid):
    shopping_list = dbpm.QUY_Shopping_Cart_by_scid(scid)
    if(not shopping_list):
        return False, "購物車內沒有商品喔"
    for prod in shopping_list:
        current_quantity = dbpm.QUY_Prod_Quantity_by_pid(prod[0])
        if(current_quantity - prod[1] < 0):
            dbpm.INS_UPD_Prod_to_Cart(scid, prod[0], current_quantity)
            dbpm.DEL_Shopping_Cart_items(scid)
            app.logger.warn(f"商品{prod[0]},庫存不足無法滿足訂單需求數量({prod[1]})")
            return False, "部分商品庫存不足,請稍後重試"
    return True, None
def MakeOrder_2_Create_Order(scid, uid):
    try:
        oid = dbpm.INS_Order(uid, scid, ostatus="初始化訂單")
        return True, oid
    except ExecError as Err:
        app.logger.error(scid, uid, Err)
        return False, f"建立訂單時發生錯誤\n{Err}"
def MakeOrder_3_Request_Pay(oid, scid, paytype):
    if(Check_order_ispaid(oid)):return False, "該筆訂單已完成付款"
    shopping_list = dbpm.QUY_Shopping_Cart_by_scid(scid)
    amount = 0
    msg = None
    for cart_item in shopping_list:
        product_name, product_price = dbpm.QUY_Prod_Name_and_Price_by_pid(cart_item[0])
        amount = amount + product_price * cart_item[1]
    if(paytype == "1"):
        # ATM
        expiredate = (datetime.now() + timedelta(days = 1)).strftime("%Y%m%d")
        paid = dbpm.INS_payment_req('ATM', amount)
        neworder = APIModel.ReqOrderCreate(ShopNo=os.environ['ShopNo'], OrderNo=paid, Amount=amount*100, \
        PrdtName='IT鐵人賽虛擬商店', Param1=oid, ReturnURL=os.environ['ReturnURL'], BackendURL=os.environ['BackendURL'], PayType="A", ExpireDate=expiredate)
        msg = GenApi.OrderCreate(neworder)
        app.logger.debug(f"MakeOrder:{msg}")
    elif(paytype == "2"):
        # 信用卡一次付清
        paid = dbpm.INS_payment_req('C-1', amount)
        neworder = APIModel.ReqOrderCreate(ShopNo=os.environ['ShopNo'], OrderNo=paid, Amount=amount*100, \
        PrdtName='IT鐵人賽虛擬商店', Param1=oid, ReturnURL=os.environ['ReturnURL'], BackendURL=os.environ['BackendURL'], PayType="C")
        msg = GenApi.OrderCreate(neworder)
        app.logger.debug(f"MakeOrder:{msg}")
    if(msg):
        if(msg.Status == 'S'):
            dbpm.UPD_payment_bypaid(paid=paid, tsno=msg.TSNo, ts_decp=msg.Description, ts_status=True, cardpayurl=msg.CardParam.CardPayURL)
            dbpm.UPD_Order_by_oid(paid=paid, ostatus="已產生付款請求", oid=oid)
            isSucc, errmsg = UpdateQuantity(shopping_list)
            if(isSucc):
                return True, msg
            else:
                return False, errmsg
        else:
            dbpm.UPD_payment_bypaid(paid=paid, tsno=msg.TSNo, ts_decp=msg.Description, ts_status=False)
            dbpm.UPD_Order_by_oid(paid=paid, ostatus="產生付款請求失敗", oid=oid)
            errmsg = f"與金流系統通訊時發生錯誤,{msg.Description}"
    dbpm.UPD_Shopping_Cart_lock_bY_scid(False, scid)
    return False, errmsg or "建立付款請求時發生錯誤"
付款通知的外層的Status只是訊息傳送的Status,與付款相關的參數都在TSResultContent,所以會出現外層Status是成功,內部卻是失敗的情況,修改API解析參數調用
def OrderPayQueryHandler(resp:APIModel.ResOrderPayQuery, line_bot_api:LineBotApi):
    app.logger.debug(f"ResOrderPayQuery:{resp}")
    payinfo = resp.TSResultContent
    if(payinfo.Status != 'S'):
        dbpm.UPD_payment_bytsno(ispaid=False, paytoken=resp.PayToken, tsno=payinfo.TSNo, aptype=payinfo.APType)
        app.logger.info(f"訂單{payinfo.Param1}付款失敗, 付款編號:{payinfo.OrderNo} - {payinfo.Description}")
        uid = dbpm.UPD_Order_status_by_paid(ostatus=f"付款失敗-{payinfo.Description}", paid = payinfo.OrderNo)
        line_bot_api.push_message(uid, TextSendMessage(text=f"您的訂單: {payinfo.Param1} 付款失敗,原因可能為:\n{payinfo.Description}"))
    else:
        dbpm.UPD_payment_bytsno(ispaid=True, paytoken=resp.PayToken, tsno=payinfo.TSNo, aptype=payinfo.APType)
        app.logger.info(f"訂單{payinfo.Param1}付款成功, 付款編號:{payinfo.OrderNo} - {payinfo.Description}")
        uid = dbpm.UPD_Order_status_by_paid(ostatus=f"付款成功-{payinfo.Description}", paid = payinfo.OrderNo)
        line_bot_api.push_message(uid, TextSendMessage(text=f"您的訂單: {payinfo.Param1} 付款成功囉"))
依購物車更新庫存從建立訂單時變更,改為付款成功後才變更,比較符合常見的庫存邏輯
def UpdateQuantity(shopping_list, mode = 1):
    if(shopping_list):
        try:
            if(mode):
                for prod in shopping_list:
                    current_quantity = dbpm.QUY_Prod_Quantity_by_pid(prod[0])
                    new_quantity = current_quantity - prod[1]
                    dbpm.UPD_Prod_Quantity(prod[0], new_quantity)
                    # app.logger.debug(f"pid:{prod[0]}, oldqt:{current_quantity}, newqt:{new_quantity}")
            else:
                for prod in shopping_list:
                    current_quantity = dbpm.QUY_Prod_Quantity_by_pid(prod[0])
                    new_quantity = current_quantity + prod[1]
                    dbpm.UPD_Prod_Quantity(prod[0], new_quantity)
            return True, None
        except Exception as err:
            return False, err
    return False, f"shopping_list:{shopping_list}"
30天,鐵腿了0rz
老實說參賽最初的構想是摩斯卡App,誰知道後來變成Line訂餐機器人,只能說靈感就是這樣,因為中期想了一堆有的沒的的新功能,結果做不完哈哈
本次鐵人賽,摸了一堆以前完全沒玩過的框架與功能需求,主要有
其實最後一天還在想能加什麼功能,但想到BUG很多就還是趕緊改好品質更新,以下為想做卻還沒做的殘念部分:
| 功能 | 說明 | 大概的構想 | 
|---|---|---|
| 使用者教學 | 如何使用系統 | 拍攝影片或是給使用者一組測試的資料可供輸入 | 
| 查詢訂單 | 顯示近期訂單 | 分為一般使用者與管理人員兩種介面 | 
| 優化顯示 | Flex message | 現在的介面太醜啦 | 
| 網頁端的後台 | 控制庫存、出貨 | 目前訂單系統只寫了半套,還有一堆功能如出貨,訂單管理,之類的.....夢想太大 | 
2021鐵人賽暫時先到這邊告一段落,如果有問題,歡迎留言給我喔
Line機器人官方帳號ID:@171ssmku
